/**
 * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 */

package com.iwindplus.boot.operate.aspect;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.ObjectUtil;
import com.iwindplus.boot.domain.constant.CommonConstant.HeaderConstant;
import com.iwindplus.boot.domain.context.UserContextHolder;
import com.iwindplus.boot.domain.dto.UserCaptchaValidDTO;
import com.iwindplus.boot.domain.enums.BizCodeEnum;
import com.iwindplus.boot.domain.exception.BizException;
import com.iwindplus.boot.domain.vo.UserBaseVO;
import com.iwindplus.boot.domain.vo.UserBindVO;
import com.iwindplus.boot.domain.vo.UserCaptchaValidVO;
import com.iwindplus.boot.operate.domain.annotation.OperateValid;
import com.iwindplus.boot.operate.domain.property.OperateValidProperty;
import com.iwindplus.boot.operate.service.OperateValidService;
import com.iwindplus.boot.util.DatesUtil;
import com.iwindplus.boot.util.HttpsUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Lazy;

/**
 * 操作校验切面.
 *
 * @author zengdegui
 * @since 2024/07/06 12:18
 */
@Slf4j
@Aspect
public class OperateValidAspect {

    @Resource
    private OperateValidProperty property;

    @Lazy
    @Resource
    private OperateValidService operateValidService;

    @Pointcut("@within(com.iwindplus.boot.operate.domain.annotation.OperateValid) || @annotation(com.iwindplus.boot.operate.domain.annotation.OperateValid)")
    private void pointCutMethod() {
    }

    /**
     * Before 切面
     *
     * @param joinPoint joinPoint
     */
    @Before("pointCutMethod()")
    public void beforePointCut(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();

        OperateValid annotation = method.getAnnotation(OperateValid.class);
        if (Boolean.FALSE.equals(this.property.getEnabled())) {
            return;
        }

        if (Boolean.FALSE.equals(annotation.enabledGa())
            && Boolean.FALSE.equals(annotation.enabledMail())
            && Boolean.FALSE.equals(annotation.enabledSms())) {
            return;
        }

        final long beginMillis = System.currentTimeMillis();
        log.info("操作校验，开始时间={}", DatesUtil.parseDate(beginMillis, DatePattern.NORM_DATETIME_MS_PATTERN));

        UserBaseVO context = UserContextHolder.getContext();
        if (ObjectUtil.isEmpty(context)) {
            return;
        }

        final HttpServletRequest httpServletRequest = HttpsUtil.getHttpServletRequest();
        if (ObjectUtil.isEmpty(httpServletRequest)) {
            return;
        }

        final Long userId = context.getUserId();
        final Long orgId = context.getOrgId();
        final UserBindVO userBind = this.operateValidService.checkUserBind(userId);
        if (ObjectUtil.isEmpty(userBind)) {
            return;
        }

        try {
            // 是否开启GA校验
            String gaCaptcha = this.getGaCaptcha(annotation, httpServletRequest, userBind);

            // 是否开启邮箱校验
            String mailCaptcha = this.getMailCaptcha(annotation, httpServletRequest, userBind);

            // 是否开启短信校验
            String smsCaptcha = this.getSmsCaptcha(annotation, httpServletRequest, userBind);

            this.validateCaptcha(userId, orgId, gaCaptcha, mailCaptcha, smsCaptcha);
        } finally {
            final long endMillis = System.currentTimeMillis();
            log.info("操作校验, 结束时间={}，总执行毫秒数={}",
                DatesUtil.parseDate(endMillis, DatePattern.NORM_DATETIME_MS_PATTERN),
                endMillis - beginMillis);
        }
    }

    private String getGaCaptcha(OperateValid annotation, HttpServletRequest httpServletRequest, UserBindVO userBind) {
        if (Boolean.TRUE.equals(this.property.getEnabledGa()) && Boolean.TRUE.equals(annotation.enabledGa())) {
            if (Boolean.FALSE.equals(userBind.getGaBindFlag())) {
                throw new BizException(BizCodeEnum.GA_UNBOUND);
            }

            String gaCaptcha = httpServletRequest.getHeader(HeaderConstant.X_GA_CAPTCHA);
            if (ObjectUtil.isEmpty(gaCaptcha)) {
                throw new BizException(BizCodeEnum.GA_CAPTCHA_NOT_EMPTY);
            }
            return gaCaptcha;
        }
        return null;
    }

    private String getMailCaptcha(OperateValid annotation, HttpServletRequest httpServletRequest, UserBindVO userBind) {
        if (Boolean.TRUE.equals(this.property.getEnabledMail()) && Boolean.TRUE.equals(annotation.enabledMail())) {
            if (Boolean.FALSE.equals(userBind.getMailBindFlag())) {
                throw new BizException(BizCodeEnum.MAIL_UNBOUND);
            }

            String mailCaptcha = httpServletRequest.getHeader(HeaderConstant.X_MAIL_CAPTCHA);
            if (ObjectUtil.isEmpty(mailCaptcha)) {
                throw new BizException(BizCodeEnum.MAIL_CAPTCHA_NOT_EMPTY);
            }
            return mailCaptcha;
        }
        return null;
    }

    private String getSmsCaptcha(OperateValid annotation, HttpServletRequest httpServletRequest, UserBindVO userBind) {
        if (Boolean.TRUE.equals(this.property.getEnabledSms()) && Boolean.TRUE.equals(annotation.enabledSms())) {
            if (Boolean.FALSE.equals(userBind.getMailBindFlag())) {
                throw new BizException(BizCodeEnum.MOBILE_UNBOUND);
            }

            String smsCaptcha = httpServletRequest.getHeader(HeaderConstant.X_SMS_CAPTCHA);
            if (ObjectUtil.isEmpty(smsCaptcha)) {
                throw new BizException(BizCodeEnum.SMS_CAPTCHA_NOT_EMPTY);
            }
            return smsCaptcha;
        }
        return null;
    }

    private void validateCaptcha(Long userId, Long orgId, String gaCaptcha, String mailCaptcha, String smsCaptcha) {
        if (ObjectUtil.isEmpty(gaCaptcha) && ObjectUtil.isEmpty(mailCaptcha) && ObjectUtil.isEmpty(smsCaptcha)) {
            return;
        }

        UserCaptchaValidDTO entity = UserCaptchaValidDTO.builder()
            .userId(userId)
            .orgId(orgId)
            .gaCaptcha(gaCaptcha)
            .mailCaptcha(mailCaptcha)
            .smsCaptcha(smsCaptcha)
            .build();
        final UserCaptchaValidVO result = this.operateValidService.checkCaptchaByUserId(entity);

        log.info("操作校验, 验证结果={}", result);

        this.validateResult(result);
    }

    private void validateResult(UserCaptchaValidVO result) {
        if (result == null) {
            return;
        }

        if (Boolean.FALSE.equals(result.getGaCheckFlag())) {
            throw new BizException(BizCodeEnum.GA_CAPTCHA_ERROR);
        }
        if (Boolean.FALSE.equals(result.getMailCheckFlag())) {
            throw new BizException(BizCodeEnum.MAIL_CAPTCHA_ERROR);
        }
        if (Boolean.FALSE.equals(result.getSmsCheckFlag())) {
            throw new BizException(BizCodeEnum.SMS_CAPTCHA_ERROR);
        }
    }

}
